package com.keqiang.chart.impl.axis

import android.graphics.Color
import android.graphics.Paint
import android.graphics.PathEffect
import android.graphics.RectF
import androidx.annotation.ColorInt
import androidx.annotation.FloatRange
import androidx.annotation.IntRange
import androidx.annotation.Px
import com.keqiang.chart.utils.px

/**
 * 配置轴相关数据
 *
 * @author Created by wanggaowan on 2022/7/20 10:12
 */
open class Axis {
    /**
     * 轴线的颜色
     */
    @setparam:ColorInt
    open var lineColor: Int = Color.GRAY

    /**
     * 轴线的宽度(粗细)
     */
    @setparam:Px
    @setparam:FloatRange(from = 1.0)
    open var lineWidth: Float = 1f.px
        set(value) {
            if (value < 1f) {
                return
            }

            field = value
        }

    /**
     * 轴线样式
     */
    open var linePathEffect: PathEffect? = null

    /**
     * 作用于X轴：左边起始偏移距离。如此值设置为10 px，那么X轴标签，网格线等数据在X轴都向右偏移10px绘制
     *
     * 作用于Y轴：底部向上偏移距离。如此值设置为10 px，那么Y轴标签，网格线等数据在Y轴都向上偏移10px绘制
     */
    @setparam:Px
    open var startPadding: Float = 0f

    /**
     * 作用于X轴：右边向左偏移距离。假如X轴轴线长100，此值设置为10，那么轴线标签，网格线等数据可绘制宽度只有90
     *
     * 作用于Y轴：顶部向下偏移距离。假如Y轴轴线长100，此值设置为10，那么轴线标签，网格线等数据可绘制高度只有90
     */
    @setparam:Px
    open var endPadding: Float = 20f.px

    /**
     * 轴上标签数量
     */
    @setparam:IntRange(from = 1)
    open var labelCount: Int = 6
        set(value) {
            if (field == value) {
                return
            }

            field = if (value < 1) {
                1
            } else {
                value
            }
        }

    /**
     * 标签值文本大小
     */
    @setparam:Px
    open var labelTextSize: Float = 24f.px

    /**
     * 标签值颜色
     */
    @setparam:ColorInt
    open var labelColor: Int = Color.BLACK

    /**
     * 标签绘制距离轴线偏移值
     */
    @setparam:Px
    open var labelOffsetAxis: Float = 10f.px

    /**
     * 标签值格式化
     */
    open var axisValueFormat: AxisValueFormat = DEFAULT_VALUE_FORMAT

    /**
     * 标签对齐方式
     */
    open var labelAlign: Paint.Align = Paint.Align.CENTER

    /**
     * 标签超出一行时的对齐方式
     */
    open var labelOverAlign: Paint.Align = Paint.Align.LEFT

    /**
     * 标签最大行数
     */
    @setparam:IntRange(from = 1)
    open var labelMaxLines: Int = 1
        set(value) {
            if (value < 1) {
                return
            }
            field = value
        }

    /**
     * 标签超出最大行数后是否显示省略号,不显示省略号则直接裁断
     */
    open var labelEllipsize: Boolean = true

    /**
     * 是否绘制网格线
     */
    open var drawGridLine: Boolean = false

    /**
     * 网格线颜色
     */
    @setparam:ColorInt
    open var gridLineColor: Int = Color.GRAY

    /**
     * 网格线宽度(粗细)
     */
    @setparam:Px
    @setparam:FloatRange(from = 1.0)
    open var gridLineWidth: Float = 1f.px
        set(value) {
            if (value < 1f) {
                return
            }

            field = value
        }

    /**
     * [endPadding]属性是否作用于网格线，如果此值为true,那么X轴网格线绘制的高度将<=y轴线高度，y轴网格线绘制的宽度将<=x轴线宽度
     */
    open var gridUseEndPadding: Boolean = false

    /**
     * [startPadding]属性是否作用于网格线，如果此值为true,那么X轴网格线绘制的高度将<=y轴线高度，y轴网格线绘制的宽度将<=x轴线宽度
     */
    open var gridUseStartPadding: Boolean = false

    /**
     * 网格线样式
     */
    open var gridLinePathEffect: PathEffect? = null

    /**
     * 记录绘制的数据
     */
    val drawRectF = RectF()

    /**
     * 格式化后的标签数据
     */
    open var labelList: List<Label>? = null

    /**
     * 应用其它轴配置数据
     */
    open fun apply(axis: Axis) {
        this.lineColor = axis.lineColor
        this.lineWidth = axis.lineWidth
        this.linePathEffect = axis.linePathEffect
        this.startPadding = axis.startPadding
        this.endPadding = axis.endPadding

        this.labelCount = axis.labelCount
        this.labelTextSize = axis.labelTextSize
        this.labelColor = axis.labelColor
        this.labelOffsetAxis = axis.labelOffsetAxis
        this.axisValueFormat = axis.axisValueFormat
        this.labelAlign = axis.labelAlign
        this.labelMaxLines = axis.labelMaxLines
        this.labelOverAlign = axis.labelOverAlign
        this.labelEllipsize = axis.labelEllipsize

        this.drawGridLine = axis.drawGridLine
        this.gridLineColor = axis.gridLineColor
        this.gridLineWidth = axis.gridLineWidth
        this.gridUseEndPadding = axis.gridUseEndPadding
        this.gridLinePathEffect = axis.gridLinePathEffect

        this.drawRectF.set(axis.drawRectF)
    }

    companion object {
        val DEFAULT_VALUE_FORMAT = object : AxisValueFormat {
            override fun invoke(position: Int): Label {
                return Label.obtain(position.toString())
            }
        }
    }
}

/**
 * 标签，记录标签的文本内容及权重
 */
open class Label private constructor() {
    /**
     * 标签值
     */
    var value: String? = null
        private set

    /**
     * 标签权重，数值越大，可绘制区域越大，所有标签默认均分轴线长度。X轴均分宽度，Y轴均分高度。
     * 以X轴举例，如果[Axis.labelCount]设置为11，则标签共10等份(11个标签两两间只有10个间隔),轴线长200，则每个占20宽度。
     * 假如某个标签权重设置2，则占40宽度。此时权重为2的标签前后标签如果充满20宽度，则将出现标签重叠。
     * 一个好的解决方案是11个标签，只绘制偶数6个。如将基数位置权重设置0，偶数设置为2，这样既不会标签绘制太多，
     * 也保证绘制的标签有足够的空间
     */
    var weight: Float = 1f
        private set

    private var isRecycle: Boolean = false

    fun recycle() {
        if (isRecycle) {
            throw IllegalStateException("This $this has been recycled and cannot be recycled again")
        }

        isRecycle = true
        pool.add(this)
    }

    companion object {
        private val pool: MutableList<Label> = mutableListOf()

        /**
         * 创建一个标签对象
         *
         * @param value 标签值，传null则不绘制
         * @param weight  标签权重，数值越大，可绘制区域越大，所有标签默认均分轴线长度，X轴均分宽度，Y轴均分高度，<= 0则不绘制。
         * 以X轴举例，如果[Axis.labelCount]设置为11，则标签共10等份(11个标签两两间只有10个间隔),轴线长200，则每个占20宽度。
         * 假如某个标签权重设置2，则占40宽度。此时权重为2的标签前后标签如果充满20宽度，则将出现标签重叠。
         * 一个好的解决方案是11个标签，只绘制偶数6个。如将基数位置权重设置0，偶数设置为2，这样既不会标签绘制太多，
         * 也保证绘制的标签有足够的空间
         */
        @JvmStatic
        @JvmOverloads
        fun obtain(value: String? = null, weight: Float = 1f): Label {
            return if (pool.isEmpty()) {
                Label().apply {
                    this.value = value
                    this.weight = weight
                }
            } else {
                val label = pool[pool.size - 1]
                if (!label.isRecycle) {
                    throw IllegalStateException("This $label has not been recycled and cannot be reused")
                }

                label.isRecycle = false
                pool.removeAt(pool.size - 1)
                label.value = value
                label.weight = weight
                label
            }
        }
    }
}

/**
 * 文本格式化
 */
typealias AxisValueFormat = (position: Int) -> Label

/**
 * 配置X轴相关数据
 *
 * @author Created by wanggaowan on 2022/9/2 10:30
 */
open class BaseXAxis : Axis() {
    /**
     * 标签绘制区域高度，如果标签绘制时换行超出此高度则裁剪标签.设置为null则根据文本高度及[labelMaxHeight]、[labelMinHeight]自适应
     */
    @setparam:Px
    @setparam:FloatRange(from = 0.0)
    open var labelHeight: Float? = null
        set(value) {
            if (value != null && value < 0) {
                return
            }

            field = value
        }

    /**
     * 标签绘制区域最小高度
     */
    @setparam:Px
    @setparam:FloatRange(from = 0.0)
    open var labelMinHeight: Float = 0f
        set(value) {
            if (value < 0) {
                return
            }

            field = value
        }

    /**
     * 标签绘制最大高度，超出此值则裁剪标签，设置为null则无限制
     */
    @setparam:Px
    @setparam:FloatRange(from = 0.0)
    open var labelMaxHeight: Float? = null
        set(value) {
            if (value != null && value < 0) {
                return
            }

            field = value
        }

    /**
     * 两个标签之间的间距
     */
    @setparam:Px
    open var labelHorizontalPadding: Float = 0f

    override fun apply(axis: Axis) {
        super.apply(axis)
        if (axis is BaseXAxis) {
            this.labelHeight = axis.labelHeight
            this.labelMinHeight = axis.labelMinHeight
            this.labelMaxHeight = axis.labelMaxHeight
            this.labelHorizontalPadding = axis.labelHorizontalPadding
        }
    }
}

/**
 * 配置Y轴相关数据
 *
 * @author Created by wanggaowan on 2022/9/2 10:30
 */
open class BaseYAxis : Axis() {
    /**
     * 标签绘制宽度，超出此宽度则裁剪标签.设置为null则根据文本宽度及[labelMaxWidth]、[labelMinWidth]自适应
     */
    @setparam:Px
    @setparam:FloatRange(from = 0.0)
    open var labelWidth: Float? = null
        set(value) {
            if (value != null && value < 0) {
                return
            }

            field = value
        }

    /**
     * 标签绘制最小宽度
     */
    @setparam:Px
    @setparam:FloatRange(from = 0.0)
    open var labelMinWidth: Float = 0f
        set(value) {
            if (value < 0) {
                return
            }

            field = value
        }

    /**
     * 标签绘制最大宽度，超出此值则裁剪标签，设置为null则无限制
     */
    @setparam:Px
    @setparam:FloatRange(from = 0.0)
    open var labelMaxWidth: Float? = null
        set(value) {
            if (value != null && value < 0) {
                return
            }

            field = value
        }

    /**
     * 两个标签之间的间距
     */
    @setparam:Px
    open var labelVerticalPadding: Float = 0f

    override var labelAlign: Paint.Align = Paint.Align.RIGHT

    /**
     * 标签最大行数,如果未设置[labelWidth]或[labelMaxWidth],则始终仅绘制一行
     */
    @setparam:IntRange(from = 1)
    override var labelMaxLines: Int = super.labelMaxLines
        set(value) {
            if (value < 1) {
                return
            }
            field = value
        }

    override fun apply(axis: Axis) {
        super.apply(axis)
        if (axis is BaseYAxis) {
            this.labelWidth = axis.labelWidth
            this.labelMinWidth = axis.labelMinWidth
            this.labelMaxWidth = axis.labelMaxWidth
            this.labelVerticalPadding = axis.labelVerticalPadding
        }
    }
}